package br.com.upper.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.Queue;

import br.com.upper.business.Facade;
import br.com.upper.entity.InstanceType;
import br.com.upper.entity.TestConfiguration;
import br.com.upper.entity.TestInstance;
import br.com.upper.entity.TestInstanceResult;

public class TestManager {
		
	private static TestStatus currentStatus;	
	private static TestConfiguration testConfiguration;
	private static Queue<TestInstance> otherTests;
	private static TestInstance mainTest;
	public static InstanceType bestInstance;
	
	static {
		currentStatus = new TestStatus();
		otherTests = new LinkedList<TestInstance>();
	}
	
	public static void restart(TestConfiguration configuration) {		
		currentStatus = new TestStatus();	 	
		testConfiguration = configuration;
		mainTest = new TestInstance(configuration, configuration.getInstanceType(), true);
		otherTests = new LinkedList<TestInstance>();
		if(configuration.isFindBestInstanceType()) {
			List<InstanceType> otherInstanceTests = Facade.getInstance().findOtherInstancesTest();
			for(InstanceType instance: otherInstanceTests) {
				otherTests.add(new TestInstance(configuration, instance, false));
			}
		}								
	}
	
	public static TestStatus checkStatus() {
		
		TestStep step = currentStatus.getCurrentStep();
		TestInstanceStep instanceStep = getTestInstanceStep();
		
		System.out.println("Test Status: " + step.getDescription());
		if(instanceStep != null) {
			System.out.println("Test Instance Status: " + instanceStep.getDescription());
		} else {
			System.out.println("Test Instance Status: null");
		}		
		
		if(step == TestStep.STOPPED || (step == TestStep.TESTING_MAIN_INSTANCE && checkTestMainInstanceFinished()) || 
				(step == TestStep.TESTING_OTHER_INSTANCE && checkTestOtherInstanceFinished()) ||
				((step == TestStep.TESTING_MAIN_INSTANCE || step == TestStep.TESTING_OTHER_INSTANCE) &&
				(instanceStep == TestInstanceStep.STOPPED || (instanceStep == TestInstanceStep.RUNNING_INSTANCE && checkInstanceRunningFinished()) || 
				(instanceStep == TestInstanceStep.EXECUTING_TEST && checkTestExecutingFinished()) || 
				(instanceStep == TestInstanceStep.TERMINATING_INSTANCE && checkInstanceTerminatingFinished()) ||
				(instanceStep == TestInstanceStep.ANALYZING_LOG && checkAnalyzingLogFinished()))) ||
				(step == TestStep.TESTING_OTHER_INSTANCE && instanceStep == TestInstanceStep.FINISHED)) {
			
			nextStep();
			
		} else if ((step == TestStep.TESTING_MAIN_INSTANCE || step == TestStep.TESTING_OTHER_INSTANCE) && instanceStep == TestInstanceStep.EXECUTING_TEST) {
			getTestInstanceStatus().setLog(JMeterUtil.getTestLog());
		}
		
		return currentStatus;
	}

	private static void nextStep() {
		
		TestStep step = currentStatus.getCurrentStep();
		TestInstanceStep instanceStep = getTestInstanceStep();
		
		if(step == TestStep.STOPPED) {
//			startOtherTest();
			startMainTest();
		} else if (step == TestStep.TESTING_MAIN_INSTANCE) {			
			if (instanceStep == TestInstanceStep.RUNNING_INSTANCE) {
				updatePropertieFile();
				executeTest();			
			} else if (instanceStep == TestInstanceStep.EXECUTING_TEST) {
				terminateInstance();
			} else if (instanceStep == TestInstanceStep.TERMINATING_INSTANCE) {
				analyzeLog();
			} else if (instanceStep == TestInstanceStep.ANALYZING_LOG) {
				if(LogProcessor.logRunnable.result == null) {
					bestInstance = currentStatus.getCurrentTestInstance().getInstanceType();
					finishTest();
				} else {
					getTestInstanceStatus().setResult(LogProcessor.logRunnable.result);
//					getTestInstanceStatus().setResult(getFakeResult());
					getTestInstanceStatus().getResult().setInstanceType(currentStatus.getCurrentTestInstance().getInstanceType());
					Facade.getInstance().saveTestInstanceResult(getTestInstanceStatus().getResult());
					finishMainTest();
				}	

			} else if (instanceStep == TestInstanceStep.FINISHED) {
				if(testConfiguration.isFindBestInstanceType() && otherTests.size() > 0) {
					startOtherTest();
				} else {
					finishTest();
				}
			}
		} else if (step == TestStep.TESTING_OTHER_INSTANCE) {
			if (instanceStep == TestInstanceStep.RUNNING_INSTANCE) {
				updatePropertieFile();
				executeTest();			
			} else if (instanceStep == TestInstanceStep.EXECUTING_TEST) {
				terminateInstance();
			} else if (instanceStep == TestInstanceStep.TERMINATING_INSTANCE) {
				analyzeLog();
			} else if (instanceStep == TestInstanceStep.ANALYZING_LOG) {
				if(LogProcessor.logRunnable.result == null) {
					bestInstance = currentStatus.getCurrentTestInstance().getInstanceType();
					finishTest();
				} else {
					getTestInstanceStatus().setResult(LogProcessor.logRunnable.result);					
//					getTestInstanceStatus().setResult(getFakeResult());				
					getTestInstanceStatus().getResult().setInstanceType(currentStatus.getCurrentTestInstance().getInstanceType());
					Facade.getInstance().saveTestInstanceResult(getTestInstanceStatus().getResult());
					finishOtherTest();
				}
			} else if (instanceStep == TestInstanceStep.FINISHED) {
				if(otherTests.size() > 0) {
					startOtherTest();
				} else {
					finishTest();
				}
			} 
		}  
	}
	
	private static void updatePropertieFile() {
		
		String resources = testConfiguration.getLogResourcesPath();
		resources = resources.substring(0, resources.indexOf(".csv")) +  "-" + System.currentTimeMillis() + ".csv";
		currentStatus.getCurrentTestInstance().setCurrentLogResources(resources);
		
		String transactions = testConfiguration.getLogTransactionsPath();
		transactions = transactions.substring(0, transactions.indexOf(".csv")) +  "-" + System.currentTimeMillis() + ".csv";
		currentStatus.getCurrentTestInstance().setCurrentLogTransaction(transactions);
		
			try {
				String propertiePath = testConfiguration.getJMeterDirectory() + File.separator + "bin" + File.separator + "user.properties";
				
		        FileInputStream in = new FileInputStream(propertiePath);
		        Properties props = new Properties();
		        props.load(in);
		        in.close();

		        FileOutputStream out = new FileOutputStream(propertiePath);	
		        props.setProperty(testConfiguration.getLogTransactionsPathKey(),currentStatus.getCurrentTestInstance().getCurrentLogTransaction());
		        props.setProperty(testConfiguration.getLogResourcesPathKey(),currentStatus.getCurrentTestInstance().getCurrentLogResources());        
		        props.setProperty(testConfiguration.getDbDriverJMeterKey(),testConfiguration.getDbDriver());
		        props.setProperty(testConfiguration.getDbUserJMeterKey(),testConfiguration.getDbUser());
		        props.setProperty(testConfiguration.getDbPasswordJMeterKey(),testConfiguration.getDbPassword());
		        props.setProperty(testConfiguration.getDbMaxConnJMeterKey(),testConfiguration.getDbMaxConn());
		        props.setProperty(testConfiguration.getDbTimeOutGroupJMeterKey(),testConfiguration.getDbTimeOutGroup());
		        props.setProperty(testConfiguration.getDbCleanIdleIntervalJMeterKey(),testConfiguration.getDbCleanIdleInterval());
		        props.setProperty(testConfiguration.getDbConnTimeoutJMeterKey(),testConfiguration.getDbConnTimeout());
		        props.setProperty(testConfiguration.getDbTestQueryJMeterKey(),testConfiguration.getDbTestQuery());
		        props.setProperty(testConfiguration.getNumThreadsJMeterKey(),testConfiguration.getNumThreads().toString());
		        props.setProperty(testConfiguration.getInitTimeJMeterKey(),testConfiguration.getInitTime().toString());
		        props.setProperty(testConfiguration.getIterationsJMeterKey(),testConfiguration.getIterations().toString());		     
		        props.setProperty(testConfiguration.getDbURLJMeterKey(),testConfiguration.getDbUrl().replace("[host]", AWSUtil.getIP()));
//		        props.setProperty(testConfiguration.getDbURLJMeterKey(),testConfiguration.getDbUrl().replace("[host]", "127.0.0.1"));
		        props.setProperty("host", AWSUtil.getIP());
//		        props.setProperty("host", "127.0.0.1");
		        props.store(out, null);
		        out.close();
			} catch(IOException e) {
				System.out.println("Error upadting propertie");
			}		
	}
	
	private static void startMainTest() {
		currentStatus.setCurrentStep(TestStep.TESTING_MAIN_INSTANCE);
		currentStatus.setCurrentTestInstance(mainTest);
		runInstance();
	}
	
	private static void startOtherTest() {
		currentStatus.setCurrentStep(TestStep.TESTING_OTHER_INSTANCE);
		currentStatus.setCurrentTestInstance(otherTests.poll());
		runInstance();
	}
	
	private static void runInstance() {
		getTestInstanceStatus().setCurrentStep(TestInstanceStep.RUNNING_INSTANCE);
		AWSUtil.runInstance(testConfiguration, currentStatus.getCurrentTestInstance().getInstanceType());
	}
	
	private static void executeTest() {
		getTestInstanceStatus().setCurrentStep(TestInstanceStep.EXECUTING_TEST);
		JMeterUtil.startTest(testConfiguration);
	}
	
	private static void terminateInstance() {
		getTestInstanceStatus().setCurrentStep(TestInstanceStep.TERMINATING_INSTANCE);
		AWSUtil.terminateInstance();
	}
	
	private static void analyzeLog() {
		getTestInstanceStatus().setCurrentStep(TestInstanceStep.ANALYZING_LOG);
		LogProcessor.process(testConfiguration, currentStatus.getCurrentTestInstance().getCurrentLogResources(), currentStatus.getCurrentTestInstance().getCurrentLogTransaction());
	}
	
	private static void finishMainTest() {
		getTestInstanceStatus().setCurrentStep(TestInstanceStep.FINISHED);		
	}
	
	private static void finishOtherTest() {
		getTestInstanceStatus().setCurrentStep(TestInstanceStep.FINISHED);		
	}
	
	private static void finishTest() {
		getTestInstanceStatus().setCurrentStep(TestInstanceStep.FINISHED);
		currentStatus.setCurrentStep(TestStep.FINISHED);
	}
	
	private static boolean checkTestMainInstanceFinished() {		
		TestStep step = currentStatus.getCurrentStep();
		TestInstanceStep instanceStep = getTestInstanceStep();
		if(step.getIndex() > TestStep.TESTING_MAIN_INSTANCE.getIndex()) {
			return true;
		} else if (step == TestStep.TESTING_MAIN_INSTANCE && instanceStep == TestInstanceStep.FINISHED) {
			return true;
		}
		
		return false;
//		return true;
	}
	
	private static boolean checkTestOtherInstanceFinished() {		
		TestStep step = currentStatus.getCurrentStep();
		TestInstanceStep instanceStep = getTestInstanceStep();
		if(step.getIndex() > TestStep.TESTING_OTHER_INSTANCE.getIndex()) {
			return true;
		} else if (step == TestStep.TESTING_OTHER_INSTANCE && instanceStep == TestInstanceStep.FINISHED && otherTests.size() == 0) {
			return true;
		}
		
		return false;

	}
	
	private static boolean checkInstanceRunningFinished() {
		
		TestInstanceStep step = getTestInstanceStep();
		
		if(step.getIndex() > TestInstanceStep.RUNNING_INSTANCE.getIndex()) {
			return true;
		} else if(step == TestInstanceStep.RUNNING_INSTANCE) {
			return AWSUtil.checkInstanceRunning();
//			return true;
		}
		
		return false;
	}
	
	private static boolean checkTestExecutingFinished() {
		
		TestInstanceStep step = getTestInstanceStep();
		
		if(step.getIndex() > TestInstanceStep.EXECUTING_TEST.getIndex()) {
			return true;
		} else if(step == TestInstanceStep.EXECUTING_TEST) {

			return !JMeterUtil.checkTestRunning();
//			return true;
		}
		
		return false;

		
	}
	
	private static boolean checkInstanceTerminatingFinished() {
		
		TestInstanceStep step = getTestInstanceStep();
		
		if(step.getIndex() > TestInstanceStep.TERMINATING_INSTANCE.getIndex()) {
			return true;
		} else if(step == TestInstanceStep.TERMINATING_INSTANCE) {
			return true;
		}
		
		return false;
		
	}
	
	private static boolean checkAnalyzingLogFinished() {
		
		TestInstanceStep step = getTestInstanceStep();
		
		if(step.getIndex() > TestInstanceStep.ANALYZING_LOG.getIndex()) {
			return true;
		} else if(step == TestInstanceStep.ANALYZING_LOG) {
			
			return !LogProcessor.logRunnable.running;
//			return true;
		}
		
		return false;		
	}
	
	private static boolean checkTestFinished() {
		
		TestInstanceStep step = getTestInstanceStep();
		
		if(step == TestInstanceStep.FINISHED) {
			return true;
		} 		
		return false;		
	}
	
	private static TestInstanceStep getTestInstanceStep() {
		if (getTestInstanceStatus() != null) {
			return getTestInstanceStatus().getCurrentStep();
		}
		return null;
	}
	
	private static TestInstanceStatus getTestInstanceStatus() {
		if(currentStatus.getCurrentTestInstance() != null) {
			return currentStatus.getCurrentTestInstance().getCurrentStatus();
		}
		return null;
	}
	
	private static TestInstanceResult getFakeResult() {
		
		TestInstanceResult result = new TestInstanceResult();
		result.setCpuMetric(100.0D);
		result.setData(new Date());
		result.setDiskMetric(100D);
		result.setInstanceType(currentStatus.getCurrentTestInstance().getInstanceType());
		result.setMaxThreads(10);
		result.setMemoryMetric(100.0D);
		result.setProvisionedIOPs(currentStatus.getCurrentTestInstance().getTestConfiguration().getProvisionedIOPS());
		result.setTransactionExecTime(100L);
		result.setTransactionMaxExecTime(50L);
		result.setTransactionName("Transacao test");
		result.setTransactionTotalExecutions(10);
		
		return result;
		
	}
	

}
